package com.frank.loadinglibrary.common.element;

import com.frank.loadinglibrary.base.baseanimations.BaseAnimationController;
import com.frank.loadinglibrary.base.baseelement.BaseElement;
import com.frank.loadinglibrary.base.baseelement.IComponentCallback;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;

import java.util.ArrayList;

public class FoldingCirclesElement extends BaseElement implements BaseAnimationController.IAnimationController<AnimatorValue> {

    private static final float MAX_LEVEL = 10000;
    private static final float CIRCLE_COUNT = ProgressStates.values().length;
    private static final float MAX_LEVEL_PER_CIRCLE = MAX_LEVEL / CIRCLE_COUNT;
    private static final int ALPHA_OPAQUE = 255;
    private static final int ALPHA_ABOVE_DEFAULT = 235;


    //记录当前状态
    private ProgressStates mCurStates = ProgressStates.FOLDING_DOWN;
    private RectFloat mOval = new RectFloat();
    private Path mPath;
    private float mHalfX;
    private float mHalfY;

    //把绘制区域分为2个区域
    private Paint mFirstHalfPaint;
    private Paint mSecondHalfPaint;
    private Paint mPathPaint;


    private float mAxisValue;

    private float mControlPointMinimum;
    private float mControlPointMaximum;

    private int mAlpha = ALPHA_OPAQUE;
    private Color mColor1;
    private Color mColor2;
    private Color mColor3;
    private Color mColor4;
    private Color fstColor, scndColor;
    private boolean goesBackward;

    private FoldingCirclesParams params;
    private final int valueEight = 8;

    public FoldingCirclesElement(FoldingCirclesParams params, IComponentCallback call) {
        super();
        this.callback = call;
        this.params = params;
    }

    @Override
    public void initPaint() {
        super.initPaint();
        initColors(params.colors);
        mPath = new Path();

        Paint basePaint = new Paint();
        basePaint.setAntiAlias(true);
        basePaint.setStyle(Paint.Style.FILL_STYLE);
        mFirstHalfPaint = new Paint(basePaint);
        mSecondHalfPaint = new Paint(basePaint);
        mPathPaint = new Paint(basePaint);

        setAlpha(mAlpha);
    }


    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        measureCircleProgress(getWidth(), getHeight());
        switch (mCurStates) {
            case FOLDING_DOWN:
            case FOLDING_UP:
                drawYMotion(canvas);
                break;
            case FOLDING_RIGHT:
            case FOLDING_LEFT:
                drawXMotion(canvas);
                break;
            default:
                break;
        }
        canvas.drawPath(mPath, mPathPaint);
    }

    @Override
    public ArrayList<AnimatorValue> onCreateAnimators() {
        ArrayList<AnimatorValue> animators = new ArrayList<AnimatorValue>();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener((animatorValue1, v) -> {
            int value = (int) (10000 * v);
            update(value);
            if (callback != null) {
                callback.reflushComponent();
            }
        });
        animatorValue.setDuration(params.durtion);
        animatorValue.setLoopedCount(-1);
        animatorValue.setCurveType(Animator.CurveType.LINEAR);
        animators.add(animatorValue);
        return animators;
    }

    @Override
    public void onAnimationReset() {
        //do nothing because donot need to reset animation params
    }

    @Override
    public BaseAnimationController.IAnimationController<AnimatorValue> initAnimationController() {
        return this;
    }

    private void measureCircleProgress(float width, float height) {
        if (params.mDiameter <= 0) {
            params.mDiameter = Math.min(width, height);
        }
        float mDiameter = params.mDiameter;
        mHalfX = width / 2;
        mHalfY = height / 2;
        if (mOval == null) {
            mOval = new RectFloat(width / 2 - mDiameter / 2, height / 2 - mDiameter / 2, width / 2 + mDiameter / 2, height / 2 + mDiameter / 2);
        } else {
            mOval.modify(width / 2 - mDiameter / 2, height / 2 - mDiameter / 2, width / 2 + mDiameter / 2, height / 2 + mDiameter / 2);
        }
        mControlPointMinimum = -mDiameter / 6;
        mControlPointMaximum = mDiameter + mDiameter / 6;
    }

    private void drawXMotion(Canvas canvas) {
        float mDiameter = params.mDiameter;
        canvas.drawArc(mOval, new Arc(90, 180, false), mFirstHalfPaint);
        canvas.drawArc(mOval, new Arc(-270, -180, false), mSecondHalfPaint);
        mPath.reset();
        mPath.moveTo(mHalfX, getWidth() / 2 - mDiameter / 2);
        mPath.cubicTo(getWidth() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 - mDiameter / 2, getWidth() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, mHalfX, getHeight() / 2 + mDiameter / 2);
        // draw again to add its thickness, to avoid visual problem
        mPath.moveTo(mHalfX + 1, getWidth() / 2 - mDiameter / 2);
        mPath.cubicTo(getWidth() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 - mDiameter / 2, getWidth() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, mHalfX + 1, getHeight() / 2 + mDiameter / 2);
    }

    private void drawYMotion(Canvas canvas) {
        float mDiameter = params.mDiameter;
        canvas.drawArc(mOval, new Arc(0, -180, false), mFirstHalfPaint);
        canvas.drawArc(mOval, new Arc(-180, -180, false), mSecondHalfPaint);
        mPath.reset();
        mPath.moveTo(getWidth() / 2 - mDiameter / 2, mHalfY);
        mPath.cubicTo(getWidth() / 2 - mDiameter / 2, getHeight() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, getHeight() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, mHalfY);
        // draw again to add its thickness, to avoid visual problem
        mPath.moveTo(getWidth() / 2 - mDiameter / 2, mHalfY + 1);
        mPath.cubicTo(getWidth() / 2 - mDiameter / 2, getHeight() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, getHeight() / 2 - mDiameter / 2 + mAxisValue, getHeight() / 2 + mDiameter / 2, mHalfY + 1);
    }

    public void update(int value) {

        int animationLevel = value == MAX_LEVEL ? 0 : value;
        int stateForLevel = (int) (animationLevel / MAX_LEVEL_PER_CIRCLE);
        mCurStates = ProgressStates.values()[stateForLevel];

        resetColor(mCurStates);
        int levelForCircle = (int) (animationLevel % MAX_LEVEL_PER_CIRCLE);
        boolean halfPassed;
        if (!goesBackward) {
            halfPassed = levelForCircle != (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
        } else {
            halfPassed = levelForCircle == (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
            levelForCircle = (int) (MAX_LEVEL_PER_CIRCLE - levelForCircle);
        }
        String formatalpha = convertDecimalToBinary(mAlpha);
        int targetAboveAlpha = (ALPHA_ABOVE_DEFAULT * mAlpha) / ALPHA_OPAQUE;
        String formatPathalpha = convertDecimalToBinary(targetAboveAlpha);


        mFirstHalfPaint.setColor(formatColor(fstColor, formatalpha));
        mSecondHalfPaint.setColor(formatColor(scndColor, formatalpha));
        if (!halfPassed) {
            mPathPaint.setColor(formatColor(mSecondHalfPaint.getColor(), formatPathalpha));
        } else {
            mPathPaint.setColor(formatColor(mFirstHalfPaint.getColor(), formatPathalpha));
        }
        // axis
        mAxisValue = (int) (mControlPointMinimum + (mControlPointMaximum - mControlPointMinimum) * (levelForCircle / MAX_LEVEL_PER_CIRCLE));

    }

    private void resetColor(ProgressStates currentState) {
        switch (currentState) {
            case FOLDING_DOWN:
                fstColor = mColor1;
                scndColor = mColor2;
                goesBackward = false;
                break;
            case FOLDING_LEFT:
                fstColor = mColor1;
                scndColor = mColor3;
                goesBackward = true;
                break;
            case FOLDING_UP:
                fstColor = mColor3;
                scndColor = mColor4;
                goesBackward = true;
                break;
            case FOLDING_RIGHT:
                fstColor = mColor2;
                scndColor = mColor4;
                goesBackward = false;
                break;
            default:
                break;
        }
    }


    public void setAlpha(int alpha) {
        this.mAlpha = alpha;
        mFirstHalfPaint.setAlpha(alpha);
        mSecondHalfPaint.setAlpha(alpha);
        int targetAboveAlpha = (ALPHA_ABOVE_DEFAULT * alpha) / ALPHA_OPAQUE;
        mPathPaint.setAlpha(targetAboveAlpha);
    }

    public static String convertDecimalToBinary(int value) {
        return Integer.toHexString(value);
    }

    public Color formatColor(Color color, String alpha) {
        String formatColor = convertDecimalToBinary(color.getValue());
        formatColor = formatColor.replace("#", "");
        if (formatColor.length() == valueEight) {
            formatColor = formatColor.replace(formatColor.substring(0, 2), alpha);
        } else {
            formatColor = alpha + formatColor;
        }
        return new Color(Color.getIntColor("#" + formatColor));
    }

    private void initColors(Color[] colors) {
        mColor1 = colors[0];
        mColor2 = colors[1];
        mColor3 = colors[2];
        mColor4 = colors[3];
    }

    private enum ProgressStates {
        FOLDING_DOWN,
        FOLDING_LEFT,
        FOLDING_UP,
        FOLDING_RIGHT
    }

    /**
     * 构建者
     */
    public static class FoldingCirclesBuilder {
        FoldingCirclesParams params;
        IComponentCallback call;
        private final int valueFour = 4;

        public FoldingCirclesBuilder(IComponentCallback c) {
            call = c;
            params = new FoldingCirclesParams();
            setDurtion(FoldingCirclesParams.DEFALUT_DURTION);
        }

        public FoldingCirclesBuilder setDurtion(int dur) {
            params.durtion = dur;
            return this;
        }

        public FoldingCirclesBuilder setColor(String[] color) {
            if (color != null && color.length == valueFour) {
                params.colors = new Color[]{new Color(Color.getIntColor(color[0])),
                        new Color(Color.getIntColor(color[1])),
                        new Color(Color.getIntColor(color[2])),
                        new Color(Color.getIntColor(color[3]))};
            } else {
                //TODO 输出日志 提示
            }
            return this;
        }

        public FoldingCirclesBuilder setDiameter(float dia) {
            params.mDiameter = dia;
            return this;
        }

        public FoldingCirclesElement build() {
            return new FoldingCirclesElement(params, call);
        }
    }

    /***
     * 可配置参数类
     */
    public static class FoldingCirclesParams {
        private static final int DEFALUT_DURTION = 10000;
        Color[] colors = new Color[]{new Color(Color.getIntColor("#C93437")),
                new Color(Color.getIntColor("#375BF1")),
                new Color(Color.getIntColor("#F7D23E")),
                new Color(Color.getIntColor("#34A350"))};
        private int durtion = DEFALUT_DURTION;
        private float mDiameter;
    }
}
